﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Utility.DataStructure;
using Utility.Random;
using System.Diagnostics;

namespace Tests
{
	public class Test_SortedLinkedList
	{
		IRandomSource rnd => SystemRandom.Inst;

		[Test]
		public void Test_Basic ()
		{
			var list = new SortedLinkedList<float, int> ();

			//add
			FillList (list, 100);
			CollectionAssert.IsOrdered (list.Keys);
			Assert.AreEqual (list.Count, 100);

			//remove
			List<float> removedKeys = new List<float> ();
			for (int i = 0; i < 30; i++)
			{
				var kv = list.Pop ();
				removedKeys.Add (kv.Key);
			}
			CollectionAssert.IsOrdered (removedKeys);
			CollectionAssert.IsOrdered (list.Keys);
			Assert.AreEqual (removedKeys.Count, 30);
			Assert.AreEqual (list.Count, 70);

			//add again
			FillList (list, 100);
			CollectionAssert.IsOrdered (list.Keys);
			Assert.AreEqual (list.Count, 170);

			//clear
			list.Clear ();
			Assert.AreEqual (list.Count, 0);
		}

		[Test]
		public void Test_Contains ()
		{
			int[] values = Enumerable.Range (0, 100).ToArray ();
			rnd.Shuffle (values);

			var list = new SortedLinkedList<int, int> ();
			foreach (var key in values)
			{
				list.Add (key, key);
			}

			Assert.IsTrue (list.ContainsKey (0));
			Assert.IsTrue (list.ContainsKey (50));
			Assert.IsTrue (list.ContainsKey (99));

			list.Add (25, 250);
			var val = list.TryGetValue (25, out var v) ? v : -1;
			Assert.IsTrue (val == 250);
		}

		[Test]
		public void Test_MemoryUsage ()
		{
			var list = new SortedLinkedList<float, int> ();
			FillList (list, 150);
			list.Clear ();

			//add
			long m_0, m_1;
			m_0 = GC.GetTotalMemory (false);
			for (int x = 0; x < 100; x++)
			{
				FillList (list, 100);
				list.Clear ();
			}
			m_1 = GC.GetTotalMemory (false);

			Assert.AreEqual (m_0, m_1);
		}

		[Test]
		public void Test_SpeedCompare ()
		{
			Test (100);
			Test (1000);
			Test (10000);
			Test (100000);

			void Test (int itemCount)
			{
				string msg = $"{itemCount} items:\n";

				int[] values = Enumerable.Range (0, itemCount).ToArray ();
				rnd.Shuffle (values);

				Stopwatch stopwatch = new Stopwatch ();

				var linkedList = new SortedLinkedList<int, int> ();
				var dict = new SortedDictionary<int, int> ();
				var list = new SortedList<int, int> ();
				var set = new SortedSet<int> ();

				stopwatch.Start ();
				foreach (var x in values)
				{
					linkedList.Add (x, 0);
				}
				stopwatch.Stop ();
				msg += $"SortedLinkedList: \t{stopwatch.ElapsedTicks}\n";

				stopwatch.Start ();
				foreach (var x in values)
				{
					dict.Add (x, 0);
				}
				stopwatch.Stop ();
				msg += $"SortedDictionary: \t{stopwatch.ElapsedTicks}\n";

				stopwatch.Start ();
				foreach (var x in values)
				{
					list.Add (x, 0);
				}
				stopwatch.Stop ();
				msg += $"SortedList: \t{stopwatch.ElapsedTicks}\n";

				stopwatch.Start ();
				foreach (var x in values)
				{
					set.Add (x);
				}
				stopwatch.Stop ();
				msg += $"SortedSet: \t{stopwatch.ElapsedTicks}\n";


				msg += "\n";
				UnityEngine.Debug.Log (msg);
			}
		}

		void FillList (SortedLinkedList<float, int> list, int count)
		{
			for (int i = 0; i < count; i++)
			{
				list.Add (rnd.GetFloat (0, 1000), 0);
			}
		}
	}

}
